#include "board.h"
#include "bflb_gpio.h"
#include "hardware/gpio_reg.h"
#include "bflb_clock.h"
#include "mini_ws2812b.h"



// 100ns需要的指令周期数
uint16_t ws2812b_delay_ns = 0;

// 灯珠颜色数据
static uint8_t *ws2812b_addr = NULL;
// 灯珠颜色数据大小
static uint32_t ws2812b_size = 0;
// 灯珠数量
static uint32_t ws2812b_num = 0;
// 灯珠类型
static uint8_t ws2812b_type = WS2812B_TYPE_GRB;
// 数据引脚
static uint8_t ws2812b_pin = 0;

// GPIO控制
static struct bflb_device_s *ws2812b_gpio;
static uint32_t ws2812b_pin_addr = 0;
static uint32_t ws2812b_pin_high = 0;
static uint32_t ws2812b_pin_low = 0;

// 纳秒级延时 单位:100ns
// ATTR_TCM_SECTION 将此方法代码放入高速缓存中,提高运行效率,保证延时精度.
void __WEAK ATTR_TCM_SECTION ws2812b_delay100ns(uint32_t time)
{
    // time *= ws2812b_delay_ns;
    // // 减去额外的4条指令
    // time -= 4;
    // while (time)
    // {
    //     __ASM __volatile("nop");
    //     time--;
    // }
    __ASM __volatile(
        "lhu a5,ws2812b_delay_ns\n\t"
        "mul a0,a5,a0\n\t"
        "addi a0,a0,-4\n\t"
        "beqz a0,wsdlend\n\t"
        "wsdlloop:"
        "nop\n\t"
        "addi a0,a0,-1\n\t"
        "bnez a0,wsdlloop\n\t"
        "wsdlend:"
        "ret\n\t");
}

// 发送数据
// ATTR_TCM_SECTION 将此方法代码放入高速缓存中,提高运行效率,保证时序准确.
void ATTR_TCM_SECTION ws2812b_flush()
{
    uint8_t *addr;
    uint8_t value;
    // 遍历所有灯珠
    for (uint32_t i = 0; i < ws2812b_num; i++)
    {
        // 计算i灯珠颜色数据地址
        addr = ws2812b_addr + i * ws2812b_type;
        // 遍历每个颜色值
        for (uint8_t j = 0; j < ws2812b_type; j++)
        {
            // 取出颜色值 1字节
            value = *(addr + j);
            // 遍历每个位
            for (uint8_t k = 0; k < 8; k++)
            {
                // 0x80 二进制就是1000 0000,把value跟它进行位于操作就是判断最高位是否为1
                if (value & 0x80)
                {
                    // 1码
                    // 输出0.6us低电平
                    putreg32(ws2812b_pin_addr, ws2812b_pin_high);
                    ws2812b_delay100ns(6);
                    // 输出0.3us低电平
                    putreg32(ws2812b_pin_addr, ws2812b_pin_low);
                    ws2812b_delay100ns(3);
                }
                else
                {
                    // 0码
                    // 输出0.2us高电平
                    putreg32(ws2812b_pin_addr, ws2812b_pin_high);
                    ws2812b_delay100ns(2);
                    // 输出0.7us低电平
                    putreg32(ws2812b_pin_addr, ws2812b_pin_low);
                    ws2812b_delay100ns(7);
                }
                // 小端 高->低
                // value左移1位 比如1111 1111左移1位变成1111 1110,就是把整体往左移动,丢掉1位高位,低位补1位0
                value <<= 1;
            }
        }
    }
}
// #pragma GCC pop_options

// 初始化
void ws2812b_init(uint8_t pin, uint32_t num, uint8_t type)
{
    ws2812b_deinit();
    // 根据主频计算100ns需要的指令周期数
    ws2812b_delay_ns = 100.0 / (1000000000.0 / bflb_clk_get_system_clock(BFLB_SYSTEM_CPU_CLK)) / 3.0;
    ws2812b_pin = pin;
    ws2812b_num = num;
    ws2812b_type = type;
    ws2812b_size = num * type;
    // 是否考虑对齐问题?
    ws2812b_addr = malloc(ws2812b_size);
    memset(ws2812b_addr, 0, ws2812b_size);
    // 初始化控制IO
    ws2812b_gpio = bflb_device_get_by_name("gpio");
    bflb_gpio_init(ws2812b_gpio, ws2812b_pin, GPIO_OUTPUT | GPIO_PULLDOWN | GPIO_SMT_DIS | GPIO_DRV_0);
    ws2812b_pin_addr = 1 << (ws2812b_pin & 0x1f);
    // bflb_gpio_set
    ws2812b_pin_high = ws2812b_gpio->reg_base + GLB_GPIO_CFG138_OFFSET + ((ws2812b_pin >> 5) << 2);
    // bflb_gpio_reset
    ws2812b_pin_low = ws2812b_gpio->reg_base + GLB_GPIO_CFG140_OFFSET + ((ws2812b_pin >> 5) << 2);
    // 复位
    ws2812b_reset();
}

// 反初始化
void ws2812b_deinit()
{
    if (!ws2812b_isReady())
    {
        return;
    }
    bflb_gpio_deinit(ws2812b_gpio, ws2812b_pin);
    free(ws2812b_addr);
    ws2812b_addr = NULL;
}

// 是否初始化
bool ws2812b_isReady()
{
    return ws2812b_addr != NULL;
}

// 设置单个灯珠颜色
// index 灯珠索引 0开始
// color 灯珠颜色
void ws2812b_setColor(uint32_t index, uint8_t *color)
{
    memcpy(ws2812b_addr + index * ws2812b_type, color, ws2812b_type);
}

// 设置连续多个灯珠颜色
// start 开始灯珠索引
// end 结束灯珠索引
// color 灯珠颜色
void ws2812b_setColors(uint32_t start, uint32_t end, uint8_t *color)
{
    for (uint32_t i = start; i <= end; i++)
    {
        ws2812b_setColor(i, color);
    }
}

// 设置所有灯珠颜色
void ws2812b_setColorAll(uint8_t *color)
{
    ws2812b_setColors(0, ws2812b_num - 1, color);
}

// 关闭单个灯珠
// index 灯珠索引 0开始
void ws2812b_setOff(uint32_t index)
{
    memset(ws2812b_addr + index * ws2812b_type, 0, ws2812b_type);
}

// 关闭所有灯珠
void ws2812b_setOffAll()
{
    memset(ws2812b_addr, 0, ws2812b_size);
}

// 复位(只是复位发送状态,并不会清除灯珠现有状态)
void ws2812b_reset()
{
    putreg32(ws2812b_pin_addr, ws2812b_pin_low);
    ws2812b_delay100ns(1000);
}

// 示例
void ws2812b_test()
{
    // 初始化ws2812b 数据引脚:18 灯珠数量:1 灯珠类型:GRB
    ws2812b_init(18, 1, WS2812B_TYPE_GRB);
    // 颜色变量,蓝色
    uint8_t color[3] = {0, 0, 255};
    // 设置第一个灯珠为蓝色
    ws2812b_setColor(0, color);
    // 发送数据,点亮灯珠
    ws2812b_flush();
}